#include <pjmedia/vid_codec_util.h>
#include <pjmedia/errno.h>
#include <pjmedia/stream_common.h>
#include <pjlib-util/base64.h>
#include <pj/ctype.h>
#include <pj/math.h>

#include "od_sip_types.h"
#include "od_trace.h"

#define THIS_FILE   "od_h264_sdp_match.c"


/* H264 fmtp parser */
static pj_status_t od_vid_codec_h264_parse_fmtp(    const pjmedia_codec_fmtp *fmtp,
                                                    pjmedia_vid_codec_h264_fmtp *h264_fmtp)
{
    unsigned i;
    pj_status_t status;

    pj_bzero(h264_fmtp, sizeof(*h264_fmtp));

    for (i=0; i<fmtp->cnt; ++i) {
        unsigned tmp;
        if (pj_stricmp(&fmtp->param[i].name, &STR_PROFILE_LEVEL_ID)==0) {
            /* Init H264 parameters based on level, if not set yet */
        } else if (pj_stricmp(&fmtp->param[i].name, &STR_PACKETIZATION_MODE)==0) {
            tmp = pj_strtoul(&fmtp->param[i].val);
            if (tmp <= 2) 
                h264_fmtp->packetization_mode = (pj_uint8_t)tmp;
            else
                return PJMEDIA_SDP_EINFMTP;
        } else if (pj_stricmp(&fmtp->param[i].name, &STR_MAX_MBPS)==0) {
            h264_fmtp->max_mbps = pj_strtoul(&fmtp->param[i].val);
        } else if (pj_stricmp(&fmtp->param[i].name, &STR_MAX_FS)==0) {
            h264_fmtp->max_fs = pj_strtoul(&fmtp->param[i].val);
        } else if (pj_stricmp(&fmtp->param[i].name, &STR_MAX_CPB)==0) {
            h264_fmtp->max_cpb = pj_strtoul(&fmtp->param[i].val);
        } else if (pj_stricmp(&fmtp->param[i].name, &STR_MAX_DPB)==0) {
            h264_fmtp->max_dpb = pj_strtoul(&fmtp->param[i].val);
        } else if (pj_stricmp(&fmtp->param[i].name, &STR_MAX_BR)==0) {
            h264_fmtp->max_br = pj_strtoul(&fmtp->param[i].val);
        } else if (pj_stricmp(&fmtp->param[i].name, &STR_SPROP_PARAMETER_SETS)==0)
        {
            pj_str_t sps_st;

            sps_st = fmtp->param[i].val;
            while (sps_st.slen) {
                pj_str_t tmp_st;
                int tmp_len;
                const pj_uint8_t start_code[3] = {0, 0, 1};
                char *p;
                pj_uint8_t *nal;

                /* Find field separator ',' */
                tmp_st = sps_st;
                p = pj_strchr(&sps_st, ',');
                if (p) {
                    tmp_st.slen = p - sps_st.ptr;
                    sps_st.ptr  = p+1;
                    sps_st.slen -= (tmp_st.slen+1);
                } else {
                    sps_st.slen = 0;
                }

                /* Decode field and build NAL unit for this param */
                nal = &h264_fmtp->sprop_param_sets[h264_fmtp->sprop_param_sets_len];
                tmp_len = PJ_ARRAY_SIZE(h264_fmtp->sprop_param_sets) -
                         (int)h264_fmtp->sprop_param_sets_len -
                          PJ_ARRAY_SIZE(start_code);
                status = pj_base64_decode(&tmp_st,
                                          nal + PJ_ARRAY_SIZE(start_code),
                                          &tmp_len);
                if (status != PJ_SUCCESS)
                    return PJMEDIA_SDP_EINFMTP;

                tmp_len += PJ_ARRAY_SIZE(start_code);
                pj_memcpy(nal, start_code, PJ_ARRAY_SIZE(start_code));
                h264_fmtp->sprop_param_sets_len += tmp_len;
            }
        }
    }

    return PJ_SUCCESS;
}


PJ_DEF(pj_status_t) od_codec_h264_match_sdp(pj_pool_t *pool,
                                             pjmedia_sdp_media *offer,
                                             unsigned o_fmt_idx,
                                             pjmedia_sdp_media *answer,
                                             unsigned a_fmt_idx,
                                             unsigned option)
{
    pjmedia_codec_fmtp o_fmtp_raw, a_fmtp_raw;
    pjmedia_vid_codec_h264_fmtp o_fmtp, a_fmtp;
    pj_status_t status;

    PJ_UNUSED_ARG(pool);

    /* Parse offer */
    status = pjmedia_stream_info_parse_fmtp(NULL, 
                                            offer, 
                                            pj_strtoul(&offer->desc.fmt[o_fmt_idx]),
                                            &o_fmtp_raw);
    if (status != PJ_SUCCESS)
    {
        RPTERR("error: pjmedia_stream_info_parse_fmtp failed!!");
        return status;
    }

    status = od_vid_codec_h264_parse_fmtp(&o_fmtp_raw, &o_fmtp);
    if (status != PJ_SUCCESS)
    {
        RPTERR("error: pjmedia_vid_codec_h264_parse_fmtp failed!!");
        return status;
    }

    /* Parse answer */
    status = pjmedia_stream_info_parse_fmtp(NULL, 
                                            answer, 
                                            pj_strtoul(&answer->desc.fmt[a_fmt_idx]),
                                            &a_fmtp_raw);
    if (status != PJ_SUCCESS)
    {
        RPTERR("error: pjmedia_stream_info_parse_fmtp failed!!");
        return status;
    }

    status = od_vid_codec_h264_parse_fmtp(&a_fmtp_raw, &a_fmtp);
    if (status != PJ_SUCCESS)
    {
        RPTERR("error: pjmedia_vid_codec_h264_parse_fmtp failed!!");
        return status;
    }

    RPTDBG("---------");
    RPTDBG("o_fmtp:max_mbps %d", o_fmtp.max_mbps);
    RPTDBG("o_fmtp:max_fs   %d", o_fmtp.max_fs);
    RPTDBG("a_fmtp:max_mbps %d", a_fmtp.max_mbps);
    RPTDBG("a_fmtp:max_fs   %d", a_fmtp.max_fs);

    /* format match only referance with max-mbps, and max-fs :-)*/
    if (o_fmtp.max_mbps != a_fmtp.max_mbps
            || o_fmtp.max_fs != a_fmtp.max_fs) {
        return PJMEDIA_SDP_EFORMATNOTEQUAL;
    }

    RPTDBG("MATCH!!");
    return PJ_SUCCESS;
}


/*_*/

